
/**
 * 
 * Copyright (C) 2009 DATAPREV - Empresa de Tecnologia e Informações da Previdência Social - Brasil
 *
 * Este arquivo é parte do programa SGA Livre - Sistema de Gerenciamento do Atendimento - Versão Livre
 *
 * O SGA é um software livre; você pode redistribuí­-lo e/ou modificá-lo dentro dos termos da Licença Pública Geral GNU como 
 * publicada pela Fundação do Software Livre (FSF); na versão 2 da Licença, ou (na sua opnião) qualquer versão.
 *
 * Este programa é distribuído na esperança que possa ser útil, mas SEM NENHUMA GARANTIA; sem uma garantia implícita de ADEQUAÇÃO a qualquer
 * MERCADO ou APLICAÇÃO EM PARTICULAR. Veja a Licença Pública Geral GNU para maiores detalhes.
 *
 * Você deve ter recebido uma cópia da Licença Pública Geral GNU, sob o título "LICENCA.txt", junto com este programa, se não, escreva para a 
 * Fundação do Software Livre(FSF) Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
 *
**/

package br.gov.dataprev.userinterface;

import java.awt.AWTException;
import java.awt.Cursor;
import java.awt.DisplayMode;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.ImageIcon;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;

import br.gov.dataprev.estruturadados.ConfLayout;
import br.gov.dataprev.estruturadados.ConfiguracaoGlobal;
import br.gov.dataprev.estruturadados.VetSenhas;
import br.gov.dataprev.painel.audio.AudioPlayer;
import br.gov.dataprev.painel.imagens.ImagesTable;

/**
 * @author DATAPREV
 * @version 1.0
 * @category Interface
 */
public class Web
{
	private static final Logger LOG = Logger.getLogger(Web.class.getName());
	
	public static final String[] FONTS_FILES  = { "Vera.ttf", "VeraBd.ttf", "VeraBI.ttf", "VeraIt.ttf", "VeraMoBd.ttf", "VeraMoBI.ttf", "VeraMoIt.ttf", "VeraMono.ttf", "VeraSe.ttf", "VeraSeBd.ttf" };
	
	public  static final String FONT_NAME = "Bitstream Vera Sans";
	
	
	private Window caixa;
	private Thread contadorDePulso;
	private JEditorPane painel;
	private int senha;
	private int intervalo = 1;
	private VetSenhas listaSenhas;
	private double multiplicador;
	
	private Robo _robo;
	
	private JLabel lSenha = new JLabel();
	private JLabel lServ = new JLabel();
	private JLabel lMesa = new JLabel();
	private JLabel lNumMesa = new JLabel();
	
	//private String confPainel;
	private Font fontBase;
	private Font fontServ;
	private Font fontMesa;
	private Font fontSenha;
	private Font fontNumMesa;
	
	private double tamServ;
	private double tamMesa;
	private double tamSenha;
	private double tamNum;
	private double tamTamLetra;
	
	private int vl, tamLetra = 74;
	
	private static Web _Instance;
	
	
	public static Web getInstance()
	{
		if (_Instance == null)
		{
			_Instance = new Web();
		}
		return _Instance;
	}
	
	/**
	 * Contrutor GUI de exibição do painel
	 */
	private Web()
	{
		Font f = null;
		for (String fontName : FONTS_FILES)
		{
			try
			{
				f = Font.createFont(Font.TRUETYPE_FONT, Web.class.getResourceAsStream("fonts/" + fontName));
				GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(f);
			}
			catch (FontFormatException e)
			{
				throw new RuntimeException("Erro interno carregando fonte padrão. ("+fontName+")", e);
			}
			catch (IOException e)
			{
				throw new RuntimeException("Erro interno carregando fonte padrão. ("+fontName+")", e);
			}
		}
		
		fontBase = new Font(Web.FONT_NAME, Font.BOLD, 235);
		
		LOG.fine("Painel Font Family: "+fontBase.getFamily());
		
		try
		{
			this.listaSenhas = new VetSenhas(30);
			// inicio do contador de tempo
			this.contadorDeTempo();
			this.init();
		}
		catch (Exception e)
		{
			LOG.log(Level.SEVERE, "Existe outro painel do SGA em execução.", e);
			Mensagem.showMensagem("O Painel SGA já está sendo executado.", "PAINEL", 1);
		}
	}
	
	public void setVisible(boolean b)
	{
		this.caixa.setVisible(b);
	}
	
	public boolean isVisible()
	{
		return this.caixa.isVisible();
	}
	
	/*
	 * Inicialização da GUI
	 */
	private void init()
	{
		GraphicsDevice[] sds = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
		GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
		if (ConfiguracaoGlobal.getInstance().isDualVideo() && sds.length > 1)
		{
			for (GraphicsDevice gd : sds)
			{
				if (gd != device)
				{
					device = gd;
					break;
				}
			}
		}
		int width = device.getDisplayMode().getWidth();
		int height = device.getDisplayMode().getHeight();
		
		LOG.info("Painel ativo em: "+device.getIDstring());
		try
		{
			setRobo(new Robo(device));
			getRobo().setDesativarProtecaoTela(ConfLayout.getInstance().getDesativarProtecaoTela());
			LOG.info("Proteção de tela desativada.");
		}
		catch (AWTException e)
		{
			LOG.log(Level.WARNING, "Não foi possível desativar proteção de tela.", e);
		}
		
		
		//this.setMultiplicador(width);
		this.setMultiplicador(height+200);
		
		
		
		if (System.getProperty("args").contains("-developer"))
		{
			this.caixa = new JFrame();
		}
		else
		{
			LOG.info("Suporte a exibição em tela cheia: "+device.isFullScreenSupported());
			DisplayMode dm = device.getDisplayMode();
			
			LOG.info("Modo de exibição atual: "+dm.getWidth()+"x"+dm.getHeight()+" "+dm.getBitDepth()+"bpp "+dm.getRefreshRate()+"hz");
			
			boolean fullscreenOk = this.criaFullscreen(device);
			if (!fullscreenOk)
			{
				this.emularFullScreen(device);
			}
		}
		
		
		this.painel = new JEditorPane();
		
		String msgServ = "Atendimento", numSenha = "A0139", guiche = "Guichê:", numero = "000";
		
		tamServ = 100 * this.multiplicador;
		fontServ = new Font(Web.FONT_NAME, Font.BOLD, (int) tamServ);
		System.err.println("FONT: "+fontServ.toString()+" FACENAME: "+fontServ.getFontName());
		tamMesa = 120 * this.multiplicador;
		fontMesa = new Font(Web.FONT_NAME, Font.BOLD, (int) tamMesa);
		tamSenha = 210 * this.multiplicador;
		fontSenha = new Font(Web.FONT_NAME, Font.BOLD, (int) tamSenha);
		tamNum = 210 * this.multiplicador;
		fontNumMesa = new Font(Web.FONT_NAME, Font.BOLD, (int) tamNum);
		tamTamLetra = 74 * this.multiplicador;
		tamLetra = (int) tamTamLetra;
		
		this.senha = this.listaSenhas.ultimoChamado();
		try
		{
			if (this.senha < 0)
			{
				this.senha = 0;
				this.listaSenhas.getSenha(this.senha).setStatus(true);
			}
			msgServ = this.listaSenhas.getSenha(this.senha).getMsgEspecial();
			numSenha = this.listaSenhas.getSenha(this.senha).getSenha();
			guiche = this.listaSenhas.getSenha(this.senha).getGuiche();
			numero = this.listaSenhas.getSenha(this.senha).getNumGuiche();
		}
		catch (Exception er)
		{
			System.out.println("Fila vazia");
		}
		
		boolean linhaEscura = false;
		
		this.painel.setEditable(false);
		this.painel.setBorder(null);
		this.painel.setBounds(0, 0, width, height);
		
		this.lServ = new JLabel(msgServ);
		this.lServ.setBounds(15, 10, width, 130);
		this.lServ.setFont(this.fontServ);
		this.painel.add(this.lServ);
		
		//this.validaNumSenha(numSenha.charAt(0));
		
		this.lSenha = new JLabel(numSenha);
		this.lSenha.setAlignmentX(JLabel.CENTER_ALIGNMENT);
		this.lSenha.setBounds(15, (int) ((double) 20 * this.multiplicador), (int) ((double) 900 * this.multiplicador), (int) ((double) 500 * this.multiplicador));
		this.lSenha.setFont(this.fontSenha);
		this.painel.add(this.lSenha);
		ImageIcon img = ImagesTable.getInstance().getImageIcon("degrade_background.png", width, (int) ((double) 260 * this.multiplicador));
		
		JLabel degradeBack = new JLabel(img);
		degradeBack.setBounds(0, (int) ((double) 140 * this.multiplicador), width, (int) ((double) 260 * this.multiplicador));
		this.painel.add(degradeBack);
		
		String localArq = (linhaEscura) ? "linhaAzul.png" : "linha.png";
		img = ImagesTable.getInstance().getImageIcon(localArq, width, 4);
		JLabel linha1 = new JLabel(img);
		linha1.setBounds(0, (int) ((double) 137 * this.multiplicador), width, 4);
		this.painel.add(linha1);
		
		JLabel linha2 = new JLabel(img);
		linha2.setBounds(0, (int) ((double) 400 * this.multiplicador), width, 4);
		this.painel.add(linha2);
		
		this.lMesa = new JLabel(guiche);
		this.lMesa.setBounds(2, (int) ((double) 458 * this.multiplicador), (int) ((double) 1024 * this.multiplicador), (int) ((double) 180 * this.multiplicador));
		
		if (guiche.length() >= 6 && numero.length() >= 3)
		{
			tamMesa = 100 * this.multiplicador;
			fontMesa = new Font(Web.FONT_NAME, Font.BOLD, (int) tamMesa);
			tamNum = 150 * this.multiplicador;
			fontNumMesa = new Font(Web.FONT_NAME, Font.BOLD, (int) tamNum);
			tamLetra = (int) (65 * this.multiplicador);
		}
		
		this.lMesa.setFont(this.fontMesa);
		
		this.painel.add(this.lMesa);
		
		this.lNumMesa = new JLabel(numero);
		
		vl = this.tamLetra * 7;
		
		tamLetra = (int) (74 * this.multiplicador);
		
		this.lNumMesa.setBounds(this.vl, (int) ((double) 438 * this.multiplicador), (int) ((double) 1024 * this.multiplicador), (int) ((double) 180 * this.multiplicador));
		this.lNumMesa.setFont(this.fontNumMesa);
		aplicarLayout();
		
		this.painel.add(this.lNumMesa);
		
		this.caixa.add(this.painel);
		
		setAlwaysOnTop(true);
		this.caixa.setVisible(true);
		this.caixa.setSize(width, height);
		
		this.painel.addMouseListener(new PainelMouseListener());
		
		this.exibirSenha(msgServ, guiche, "000", "-----" );
	}
	
	private boolean criaFullscreen(GraphicsDevice device)
	{
		if (!device.isFullScreenSupported() || !device.isDisplayChangeSupported())
		{
			return false;
		}
		
		this.caixa = new JFrame(device.getDefaultConfiguration());
		device.setFullScreenWindow(this.caixa);
		device.setDisplayMode(device.getDisplayMode());
		LOG.info("Tela Cheia OK: "+(device.getFullScreenWindow() == this.caixa));
		
		return device.getFullScreenWindow() == this.caixa;
	}
	
	private void emularFullScreen(GraphicsDevice device) {
		LOG.info("Emulando Tela Cheia...");
		this.caixa = new JWindow(device.getDefaultConfiguration());
		device.setFullScreenWindow(this.caixa);
	}
	
	// metodo que faz a senha piscar
	private class PiscaLayout extends Thread 
	{
		
		@Override
		public void run()
		{
			final Runnable r = new Runnable()
			{

				@Override
				public void run()
				{
					//System.err.println("PISCA: "+!lSenha.isVisible());
					lSenha.setVisible(!lSenha.isVisible());
				}
				
			};
			
			// o total deve ser PAR, senão o texto fica escondido ao final do loop
			for (int i = 0; i < 6; i++)
			{
				// alteracoes em componentes da Swing devem ser feitos pelo Thread da Swing
				SwingUtilities.invokeLater(r);
				
				// Aguarda um tempo antes de alterar o estado
				// criando o efeito de pisca-pisca
				try
				{
					Thread.sleep(300);
				}
				catch (InterruptedException e)
				{
					// nada
				}
			}
		}
		
	}
	
	/**
	 * abilita ou desabilita ponteiro de mouse
	 * 
	 * @param flag
	 */
	
	private void setCursor(boolean flag)
	{
		if (flag)
		{
			// apaga o cursor do mouse passando uma imagem inexistente
			Image imagemCursor = Toolkit.getDefaultToolkit().getImage("INVALIDA.gif");
			Cursor cursorTransparente = Toolkit.getDefaultToolkit().createCustomCursor(imagemCursor, new Point(0, 0), "");
			this.caixa.setCursor(cursorTransparente);
		}
		else
		{
			this.caixa.setCursor(Cursor.getDefaultCursor());
		}
	}
	
	/**
	 * Desaloca o objeto web da mem�ria
	 */
	public void close()
	{
		this.caixa.dispose();
	}
	
	private void contadorDeTempo()
	{
		//this.contadorDePulso = new Thread(new atualizaTempo(), "Tempo de Atualizacao");
		this.contadorDePulso = new Thread(new UDPListener(), "UDP Receptor");
		this.contadorDePulso.setDaemon(true);
		this.contadorDePulso.start();
	}
	
	private void exibirSenha(String _msgEspecial, String _guiche, String _numeroGuiche, String _senha)
	{
		lServ.setText(_msgEspecial);
		lMesa.setText(_guiche);
		lNumMesa.setText(_numeroGuiche);
		String numSenha = _senha;
		
		int charWidth = lSenha.getFontMetrics(fontBase).stringWidth("Z0138");
		int fontSize = (int) Math.round(235 * (771.0 / charWidth));
		charWidth = lSenha.getFontMetrics(fontBase).stringWidth( _senha);
		fontSize = (int) Math.floor(235 * (771.0 / charWidth) * Web.this.multiplicador);
		
		fontSenha = new Font(Web.FONT_NAME, Font.BOLD, fontSize);
		
		lSenha.setFont(fontSenha);
		lSenha.setText(numSenha);
	}
	
	class SenhaPainel implements Runnable
	{
		private final String _msgEspecial;
		private final String _senha;
		private final String _guiche;
		private final String _numeroGuiche;

		public SenhaPainel(String msgEspecial, char charServico, int senha, String guiche, int numeroGuiche)
		{
			_msgEspecial = msgEspecial;
			
			String senhaStr = String.valueOf(senha);
			// completa com os zeros necessarios
			for (int i = senhaStr.length(); i < 4; i++)
			{
				senhaStr = "0"+senhaStr;
			}
			
			_senha = charServico+senhaStr;
			_guiche = guiche+':';
			
			String numGuicheStr = String.valueOf(numeroGuiche);
			// completa com os zeros necessarios
			for (int i = numGuicheStr.length(); i < 3; i++)
			{
				numGuicheStr = "0"+numGuicheStr;
			}
			_numeroGuiche = numGuicheStr;
		}
		
		/* (non-Javadoc)
		 * @see java.lang.Runnable#run()
		 */
		@Override
		public void run()
		{
				Web.this.exibirSenha(_msgEspecial, _guiche, _numeroGuiche, _senha);
				
				PiscaLayout t = new PiscaLayout();
				t.start();
				
				try
				{				
					AudioPlayer.getInstance().play(AudioPlayer.ALERTS_PATH, ConfLayout.getInstance().getSom());
				}
				catch (Throwable tw)
				{
					LOG.log(Level.SEVERE, "Erro tocando som: "+ConfLayout.getInstance().getSom(), tw);
				}
				
				if (ConfLayout.getInstance().isVocalizarSenhas())
				{
					try
					{
						AudioPlayer.getInstance().getVocalizador().vocalizar("senha", true);
						
						for (int i = 0; i < _senha.length(); i++)
						{
							AudioPlayer.getInstance().getVocalizador().vocalizar(String.valueOf(_senha.charAt(i)), true);
						}
						
						AudioPlayer.getInstance().getVocalizador().vocalizar("guiche", true);
						
						for (int i = 0; i < _numeroGuiche.length(); i++)
						{
							AudioPlayer.getInstance().getVocalizador().vocalizar(String.valueOf(_numeroGuiche.charAt(i)), true);
						}
					}
					catch (Exception e1)
					{
						LOG.log(Level.SEVERE, "Erro durante vocalização de senha", e1);
					}
				}
				
				try
				{
					// Adormece o  thread para garantir que a senha atual terá tempo suficiente de exibição
					Thread.sleep(intervalo * 1000);
				}
				catch (InterruptedException e)
				{
					// nunca deverá acontecer
					e.printStackTrace();
				}
			
		}
		
		@Override
		public String toString()
		{
			return "Senha[Msg: " + _msgEspecial + " Senha: " + _senha + " GuicheMsg: " + _guiche + " GuicheNum: " + _numeroGuiche + "]";
		}
		
	}
	
	public void aplicarLayout(ConfLayout confLayout)
	{
		painel.setBackground(confLayout.getCorFundo());
		lServ.setForeground(confLayout.getCorMsgEspecial());
		lSenha.setForeground(confLayout.getCorSenha());
		lMesa.setForeground(confLayout.getCorGuiche());
		lNumMesa.setForeground(confLayout.getCorGuiche());
	}
	
	private void aplicarLayout()
	{
		this.aplicarLayout(new ConfLayout());
	}
	
	/**
	 * Verifica se existe alguma chamada de senha
	 * @return
	 */
	public int controleChamada()
	{
		return this.listaSenhas.proximoFila();
	}
	
	/**
	 * abilita e desabilita exibição no topo
	 * abilita e desabilita o ponteiro do mouse
	 * @param flag
	 */
	public void setAlwaysOnTop(boolean flag)
	{
		this.setCursor(true);
		this.caixa.setAlwaysOnTop(true);
		if (!ConfiguracaoGlobal.getInstance().isDualVideo())
		{
			this.setCursor(flag);
			this.caixa.setAlwaysOnTop(flag);
		}
	}
	
	private void setMultiplicador(double width)
	{
		this.multiplicador = width / 800.0;
	}
	
	private void setRobo(Robo robo)
	{
		_robo = robo;
	}

	public Robo getRobo()
	{
		return _robo;
	}

	class PainelMouseListener extends MouseAdapter
	{
		
		/* (non-Javadoc)
		 * @see java.awt.event.MouseAdapter#mousePressed(java.awt.event.MouseEvent)
		 */
		@Override
		public void mousePressed(MouseEvent e)
		{
			int b123mask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK |  MouseEvent.BUTTON3_DOWN_MASK; 
			
			int result = e.getModifiersEx() & b123mask;
			
			if (Integer.bitCount(result) >= 2)
			{
				Web.this.setVisible(false);
			}
			
			e.consume();
			
			LOG.fine("Painel ocultado por atalho do mouse.");
		}
	}
}